#!/usr/bin/env python3
"""
诊断监控任务重复执行问题
"""

import sys
import os
import re
from datetime import datetime, timedelta
from collections import defaultdict

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

def analyze_log_file(log_file_path):
    """分析日志文件，找出重复执行的模式"""
    print("🔍 分析监控任务重复执行问题")
    print("=" * 60)
    
    if not os.path.exists(log_file_path):
        print(f"❌ 日志文件不存在: {log_file_path}")
        return
    
    # 存储任务执行记录
    task_executions = defaultdict(list)
    beat_triggers = []
    
    # 正则表达式模式
    patterns = {
        'beat_trigger': r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*🚀 开始执行异步函数 check_single_monitor_async',
        'monitor_start': r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*开始调用煤炉.*监控ID=(\d+)',
        'monitor_success': r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*✅ 监控(\d+): 总(\d+)个, 新(\d+)个',
        'task_complete': r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*✅ check_single_monitor_async 执行成功，耗时: ([\d.]+)秒'
    }
    
    print("📖 读取日志文件...")
    with open(log_file_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()
    
    print(f"📊 共读取 {len(lines)} 行日志")
    
    # 解析日志
    for line_num, line in enumerate(lines, 1):
        # Beat触发
        if match := re.search(patterns['beat_trigger'], line):
            timestamp = match.group(1)
            beat_triggers.append({
                'timestamp': timestamp,
                'line_num': line_num
            })
        
        # 监控开始
        if match := re.search(patterns['monitor_start'], line):
            timestamp = match.group(1)
            monitor_id = int(match.group(2))
            task_executions[monitor_id].append({
                'type': 'start',
                'timestamp': timestamp,
                'line_num': line_num
            })
        
        # 监控成功
        if match := re.search(patterns['monitor_success'], line):
            timestamp = match.group(1)
            monitor_id = int(match.group(2))
            total_products = int(match.group(3))
            new_products = int(match.group(4))
            task_executions[monitor_id].append({
                'type': 'success',
                'timestamp': timestamp,
                'total_products': total_products,
                'new_products': new_products,
                'line_num': line_num
            })
    
    # 分析结果
    print("\n📋 分析结果:")
    print(f"🔄 Beat触发次数: {len(beat_triggers)}")
    print(f"📊 涉及监控数量: {len(task_executions)}")
    
    # 分析每个监控的执行情况
    print("\n🔍 监控执行详情:")
    for monitor_id, executions in task_executions.items():
        starts = [e for e in executions if e['type'] == 'start']
        successes = [e for e in executions if e['type'] == 'success']
        
        print(f"\n监控ID {monitor_id}:")
        print(f"  开始执行次数: {len(starts)}")
        print(f"  成功完成次数: {len(successes)}")
        
        if len(starts) > 1:
            print(f"  ⚠️ 发现重复执行！")
            
            # 分析时间间隔
            intervals = []
            for i in range(1, len(starts)):
                prev_time = datetime.strptime(starts[i-1]['timestamp'], '%Y-%m-%d %H:%M:%S')
                curr_time = datetime.strptime(starts[i]['timestamp'], '%Y-%m-%d %H:%M:%S')
                interval = (curr_time - prev_time).total_seconds()
                intervals.append(interval)
                print(f"    执行间隔: {interval:.1f}秒 ({starts[i-1]['timestamp']} -> {starts[i]['timestamp']})")
            
            if intervals:
                avg_interval = sum(intervals) / len(intervals)
                min_interval = min(intervals)
                max_interval = max(intervals)
                print(f"    平均间隔: {avg_interval:.1f}秒")
                print(f"    最短间隔: {min_interval:.1f}秒")
                print(f"    最长间隔: {max_interval:.1f}秒")
                
                # 判断问题类型
                if min_interval < 5:
                    print(f"    🚨 问题类型: 极短间隔重复执行 (最短{min_interval:.1f}秒)")
                elif min_interval < 30:
                    print(f"    ⚠️ 问题类型: 短间隔重复执行 (最短{min_interval:.1f}秒)")
                else:
                    print(f"    ℹ️ 问题类型: 正常间隔但可能重复 (最短{min_interval:.1f}秒)")

def analyze_beat_schedule():
    """分析Beat调度配置"""
    print("\n🕐 Beat调度配置分析:")
    print("=" * 40)
    
    # 从celery_app.py读取配置
    try:
        with open('app/celery_app.py', 'r', encoding='utf-8') as f:
            content = f.read()
        
        # 查找beat_schedule配置
        if 'beat_schedule' in content:
            print("✅ 找到Beat调度配置")
            
            # 提取monitor-health-check配置
            if '"monitor-health-check"' in content:
                print("📋 monitor-health-check任务:")
                lines = content.split('\n')
                in_monitor_config = False
                for line in lines:
                    if '"monitor-health-check"' in line:
                        in_monitor_config = True
                        print(f"  {line.strip()}")
                    elif in_monitor_config:
                        if line.strip().startswith('}') and ',' in line:
                            print(f"  {line.strip()}")
                            break
                        elif line.strip():
                            print(f"  {line.strip()}")
        else:
            print("❌ 未找到Beat调度配置")
            
    except Exception as e:
        print(f"❌ 读取配置文件失败: {e}")

def check_task_deduplication():
    """检查任务去重机制"""
    print("\n🔄 任务去重机制检查:")
    print("=" * 40)
    
    # 检查是否有任务去重逻辑
    try:
        with open('app/tasks/simple_monitor_tasks.py', 'r', encoding='utf-8') as f:
            content = f.read()
        
        # 检查关键去重机制
        checks = {
            'Redis锁机制': 'redis.*lock' in content.lower(),
            '任务状态检查': 'task.*running' in content.lower() or 'is_running' in content.lower(),
            '时间间隔检查': 'should_check_monitor' in content,
            '任务队列检查': 'apply_async' in content and 'countdown' in content,
        }
        
        for check_name, exists in checks.items():
            status = "✅" if exists else "❌"
            print(f"  {status} {check_name}: {'存在' if exists else '缺失'}")
        
        # 检查自调度逻辑
        if 'apply_async' in content and 'countdown' in content:
            print("\n🔄 发现自调度逻辑:")
            lines = content.split('\n')
            for i, line in enumerate(lines):
                if 'apply_async' in line and 'countdown' in line:
                    print(f"  行{i+1}: {line.strip()}")
        
    except Exception as e:
        print(f"❌ 检查去重机制失败: {e}")

def suggest_solutions():
    """建议解决方案"""
    print("\n💡 建议解决方案:")
    print("=" * 40)
    
    solutions = [
        {
            "问题": "Beat调度器与独立任务冲突",
            "方案": [
                "1. 调整Beat调度间隔到更长时间（如300秒）",
                "2. 或者禁用Beat调度，完全依赖独立任务自调度",
                "3. 添加任务去重机制，检查同一监控是否已有任务在队列中"
            ]
        },
        {
            "问题": "独立任务自调度重复",
            "方案": [
                "1. 添加Redis分布式锁，确保同一监控同时只有一个任务执行",
                "2. 在任务开始前检查last_check_at时间，避免重复执行",
                "3. 使用任务ID去重，避免重复调度"
            ]
        },
        {
            "问题": "任务重试机制导致重复",
            "方案": [
                "1. 检查Celery重试配置，确保重试逻辑合理",
                "2. 添加重试状态检查，避免正常任务被重试",
                "3. 优化错误处理，减少不必要的重试"
            ]
        }
    ]
    
    for i, solution in enumerate(solutions, 1):
        print(f"\n{i}. {solution['问题']}:")
        for plan in solution['方案']:
            print(f"   {plan}")

def main():
    """主函数"""
    # 分析日志文件
    log_file = "logs/monitor_20250724.log"
    analyze_log_file(log_file)
    
    # 分析Beat配置
    analyze_beat_schedule()
    
    # 检查去重机制
    check_task_deduplication()
    
    # 建议解决方案
    suggest_solutions()
    
    print("\n" + "=" * 60)
    print("🎯 诊断完成！请根据上述分析结果优化任务调度机制。")

if __name__ == "__main__":
    main()
